#include "tv_meta.h"
#include "parse_stream.h"
#include <map>
#include <sstream>

static std::multimap< tv_tok_type_e, std::vector< tv_tok_type_e > > _validation;
static std::vector< std::vector< tv_tok_type_e > > _cond_multiplexer;

static void _build_multiplexer() {
	{
		auto type = TT_QUEUE_;
		std::vector< tv_tok_type_e > mpl = { TT_EMPTY };

		for (auto tok : mpl) {
			_cond_multiplexer.push_back({ type, TT_IS, tok });
			_cond_multiplexer.push_back({ type, TT_IS_NOT, tok });
		}
	}

	{
		auto type = TT_EVENT_;
		std::vector< tv_tok_type_e > mpl = { TT_SET };

		for (auto tok : mpl) {
			_cond_multiplexer.push_back({ type, TT_IS, tok });
			_cond_multiplexer.push_back({ type, TT_IS_NOT, tok });
		}
	}
}

static void _val_pat(std::vector< tv_tok_type_e > const & pattern) {
	for (unsigned i = 0; i < pattern.size(); ++i) {
		if (pattern[i] == TPSEUDO_COND) {

			// multiplex
			for (unsigned j = 0; j < _cond_multiplexer.size(); ++j) {
				std::vector< tv_tok_type_e > copy = pattern;
				copy.erase(copy.begin() + i);

				auto & tmp = _cond_multiplexer[j];
				copy.insert(copy.begin() + i, tmp.begin(), tmp.end());
				_val_pat(copy);
			}

			// done, we may recursively call multiplex
			return;
		}
	}
	_validation.insert( std::make_pair( pattern[0], pattern ) );
}

static void _val_prep() {

	// funcs
	_val_pat({ TT_FUNC_, TT_SCOPE_OPEN });
	_val_pat({ TT_FUNC_, TT_RUNS_ON, TT_THREAD_, TT_SCOPE_OPEN });
	_val_pat({ TT_FUNC_, TT_RUNS_ON, TT_THREAD_, TT_THREAD_, TT_SCOPE_OPEN });
	_val_pat({ TT_FUNC_, TT_RUNS_ON, TT_THREAD_, TT_THREAD_, TT_THREAD_, TT_SCOPE_OPEN });

	// cond
	_val_pat({ TT_COND_, TT_SCOPE_OPEN });
	
	// conditional
	_val_pat({ TT_IF, TPSEUDO_COND, TT_SCOPE_OPEN });
	_val_pat({ TT_REQUIRE, TPSEUDO_COND });
	
	// queue functions
	_val_pat({ TT_PUSH, TT_SOME, TT_QUEUE_ });
	_val_pat({ TT_PUSH, TT_ONE, TT_QUEUE_ });
	_val_pat({ TT_POP, TT_SOME, TT_QUEUE_ });
	_val_pat({ TT_POP, TT_ONE, TT_QUEUE_ });

	// event functions
	_val_pat({ TT_RESET, TT_EVENT_ });
	_val_pat({ TT_SET, TT_EVENT_ });
	
	// misc
	_val_pat({ TT_SCOPE_CLOSE });

	// conditions
	_val_pat({ TT_VERIFY, TT_COND_ });
	_val_pat({ TT_ON_STEP, TT_COND_ });
	_val_pat({ TT_ON_IDLE, TT_COND_ });

}

void tok_prep() {
	_build_multiplexer();
	_val_prep();
}

static int _read_id(parse_stream_c & ps) {
	return ps.read_char() - 'a';
}
void tv_line_s::parse(char const * text) {
	parse_stream_c ps(text);
	raw = text;

	// skip any initial whitespace
	while (ps.starts_space())
		ps.advance(1);

	// skip white space
	while (ps.more()) {
		tv_token_s token;

		bool tok_ent = true;

		if (ps.try_read("\0")) {
			break;
		} if (ps.starts_space() ) {
			while (ps.starts_space())
				ps.advance(1);
			token.type = TT_WHITESPACE;
		} else {
				   if (ps.try_read("func_")) {
				token.type = TT_FUNC_;
			} else if (ps.try_read("event_")) {
				token.type = TT_EVENT_;
			} else if (ps.try_read("thread_")) {
				token.type = TT_THREAD_;
			} else if (ps.try_read("queue_")) {
				token.type = TT_QUEUE_;
			} else if (ps.try_read("cond_")) {
				token.type = TT_COND_;
			} else tok_ent = false;

#define TOK_LITERAL(__tok__, __type__) if( ps.try_read(__tok__) ) { token.type = __type__; } else
#define TOK_END throw std::exception("unknown token")

			if (tok_ent) {
				token.arg_int = _read_id(ps);
			} else {
				TOK_LITERAL("runs_on", TT_RUNS_ON)
				TOK_LITERAL("reset", TT_RESET)
				TOK_LITERAL("set", TT_SET)
				TOK_LITERAL("if", TT_IF)
				TOK_LITERAL("is_not", TT_IS_NOT)
				TOK_LITERAL("is", TT_IS)
				TOK_LITERAL("empty", TT_EMPTY)
				TOK_LITERAL("verify", TT_VERIFY)
				TOK_LITERAL("require", TT_REQUIRE)
				TOK_LITERAL("{", TT_SCOPE_OPEN)
				TOK_LITERAL("}", TT_SCOPE_CLOSE)
				TOK_LITERAL("push", TT_PUSH)
				TOK_LITERAL("pop", TT_POP)
				TOK_LITERAL("some", TT_SOME)
				TOK_LITERAL("one", TT_ONE)
				TOK_LITERAL("on_step", TT_ON_STEP)
				TOK_LITERAL("on_idle", TT_ON_IDLE)
				TOK_END;
			}

#undef TOK_LITERAL
#undef TOK_END
		}

		// don't keep whitespace
		if (token.type != TT_WHITESPACE)
			tokens.push_back(token);
	}

	if (!null() && !validate())
		throw std::exception("invalid line");
}

bool tv_line_s::null() const {
	return tokens.empty();
}

bool tv_line_s::validate() {
	_ASSERT(!null());
	auto valid_type = tokens[0].type;
	for (auto p = _validation.find(valid_type); p != _validation.end() && p->first == valid_type; ++p) {
		auto & against = p->second;
		if (tokens.size() == against.size()) {
			bool valid = true;
			for (unsigned i = 0; i < tokens.size(); ++i) {
				if (tokens[i].type != against[i]) {
					valid = false;
					break;
				}
			}
			if (valid)
				return true;
		}
	}
	return false;
}

std::string tv_token_s::to_string() const {
	std::stringstream result;
	if (type == TT_FUNC_) {
		result << "func_";
		result << char('a' + arg_int);
	} else if (type == TT_COND_) {
		result << "cond_";
		result << char('a' + arg_int);
	} else _ASSERT(false);
	return result.str();
}